﻿using System;
using System.Windows;

namespace Extented.UI.Core.Utils
{
    /// <summary>
    /// 直线Ax+By+C=0
    /// 已知两点P1(a1,b1),P2(a2,b2),A B C分别等于：
    /// A=b2-b1
    /// B=a1-b1
    /// C=a2*b1-a1*b2
    /// </summary>
    public class MathLine
    {
        public const double Precision = 1e-5;
        /// <summary>
        /// 直线方程Ax+By+C=0中A参数
        /// </summary>
        public double A { get; protected internal set; }
        /// <summary>
        /// 直线方程Ax+By+C=0中B参数
        /// </summary>
        public double B { get; protected internal set; }
        /// <summary>
        /// 直线方程Ax+By+C=0中C参数
        /// </summary>
        public double C { get; protected internal set; }
       

        public MathLine()
        {
            A = B = C = 0;
        }

        /// <summary>
        /// 构造函数，直线参数
        /// </summary>
        /// <param name="a">直线方程Ax+By+C=0中A参数</param>
        /// <param name="b">直线方程Ax+By+C=0中B参数</param>
        /// <param name="c">直线方程Ax+By+C=0中C参数</param>
        public MathLine(double a, double b, double c)
        {
            A = a; B = b; C = c;
        }

        /// <summary>
        /// 构造函数，直线
        /// </summary>
        /// <param name="p1">直线上一点</param>
        /// <param name="p2">直线上一点</param>
        public MathLine(Point p1, Point p2)
        {
            A = p2.Y - p1.Y;
            B = p1.X - p2.X;
            C = p2.X * p1.Y - p1.X * p2.Y;
        }

        /// <summary>
        /// 是否可用
        /// </summary>
        public bool IsValid 
        { 
            get 
            { 
                return Math.Abs(A) > Precision || Math.Abs(B) > Precision; 
            }
        }

        /// <summary>
        /// 两直线相交点
        /// </summary>
        /// <param name="line"></param>
        /// <returns></returns>
        public Point? Intersect(MathLine line)
        {
            double det = A * line.B - B * line.A;
            double x = (-C * line.B + B * line.C) / det;
            double y = (-A * line.C + C * line.A) / det;
            if (x.IsNotNumber() || y.IsNotNumber()) return null;
            return new Point(x, y);
        }

        /// <summary>
        /// 返回点到直线的垂线
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        public MathLine Perpendicular(Point p)
        {
            return new MathLine(B, -A, A * p.Y - B * p.X);
        }

        /// <summary>
        /// 返回点在直线上的投影点
        /// </summary>
        /// <param name="point"></param>
        /// <param name="line"></param>
        /// <returns></returns>
        public Point MirrorPoint(Point point)
        {
            MathLine perpendicular = Perpendicular(point);
            Point? middle = Intersect(perpendicular);
            if (!middle.HasValue) return point;
            double dx = ((Point)middle).X - point.X;
            double dy = ((Point)middle).Y - point.Y;
            return new Point(point.X + 2 * dx, point.Y + 2 * dy);
        }

        /// <summary>
        /// 是否垂直
        /// </summary>
        public bool IsVertical
        {
            get
            {
                return Math.Abs(B) < Precision;
            }
        }

        /// <summary>
        /// 是否水平
        /// </summary>
        public bool IsHorizontal 
        { 
            get 
            { 
                return Math.Abs(A) < Precision; 
            }
        }

        /// <summary>
        /// 角系数
        /// </summary>
        public double AngleCoefficient
        { 
            get 
            { 
                return -A / B; 
            }
        }

        /// <summary>
        /// 是否更接近水平
        /// </summary>
        public bool IsMoreHorizontal
        { 
            get 
            { 
                return Math.Abs(AngleCoefficient) < 1.0; 
            } 
        }

        /// <summary>
        /// 是否更接近竖直
        /// </summary>
        public bool IsMoreVertical { get { return Math.Abs(AngleCoefficient) > 1.0; } }

        /// <summary>
        /// 过两点直线与X轴夹角的ArcTan值
        /// </summary>
        public double AbscissAngleRad
        {
            get
            {
                double x = B;
                double y = -A;
                if (x < 0)
                {
                    x = -x;
                    y = -y;
                }
                if (x == 0 && y < 0)
                    y = -y;
                return Math.Atan2(y, x);
            }
        }

        /// <summary>
        /// 过两点直线与X轴夹角的角度值
        /// </summary>
        public double AbscissAngleDeg
        { 
            get 
            { 
                return AbscissAngleRad * 180 / Math.PI; 
            } 
        }

        /// <summary>
        /// 已知直线方程Ax+By+C=0及直线上一点的Y轴坐标,求该点X轴坐标。
        /// </summary>
        /// <param name="y">Y轴坐标</param>
        /// <returns>X轴坐标</returns>
        public double CalcX(double y)
        {
            return -(B * y + C) / A;
        }

        /// <summary>
        /// 已知直线方程Ax+By+C=0及直线上一点的X轴坐标,求该点Y轴坐标。
        /// </summary>
        /// <param name="y">X轴坐标</param>
        /// <returns>Y轴坐标</returns>
        public double CalcY(double x)
        {
            return -(A * x + C) / B;
        }

        /// <summary>
        /// 把点的坐标带入直线方程，用于判断直线是否过点
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        public double CalcLineFunction(Point point)
        {
            return A * point.X + B * point.Y + C;
        }

        /// <summary>
        /// 求点到直线的距离
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        public double Distance(Point point)
        {
            return Math.Abs(CalcLineFunction(point)) / Math.Sqrt(A * A + B * B);
        }

        /// <summary>
        /// 求两个点是否在直线的同侧
        /// </summary>
        /// <param name="point"></param>
        /// <param name="otherPoint"></param>
        /// <returns></returns>
        public bool IsSameSide(Point point, Point otherPoint)
        {
            return Math.Sign(CalcLineFunction(point)) == Math.Sign(CalcLineFunction(otherPoint));
        }

        /// <summary>
        /// 两点是否在直线的异侧
        /// </summary>
        /// <param name="point"></param>
        /// <param name="otherPoint"></param>
        /// <returns></returns>
        public bool IsDifferentSide(Point point, Point otherPoint)
        {
            return !IsSameSide(point, otherPoint);
        }
    }
}